#include <QJsonObject>
#include <QJsonArray>

#include <osg/Geometry>
#include <osg/BlendFunc>

#include <TopoDS_Face.hxx>
#include <BRepMesh_IncrementalMesh.hxx>
#include <CSLib.hxx>
#include <Poly_Connect.hxx>
#include <BRepTools.hxx>
#include <BRep_Tool.hxx>
#include <BRep_Builder.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS.hxx>
#include <BRepOffsetAPI_MakeOffsetShape.hxx>
#include <BRepBuilderAPI_Copy.hxx>
#include <BRepBuilderAPI_Transform.hxx>
#include <Geom_Line.hxx>
#include <Geom_Circle.hxx>
#include <GeomAdaptor_Curve.hxx>
#include <BRepAdaptor_Curve.hxx>
#include <ShapeAnalysis_Edge.hxx>
#include <BRepBuilderAPI_Transform.hxx>

#include "Surface.h"
#include "JsonReader.h"
#include "GLSLPrograms.h"
#include "OsgUtil.h"
#include "OcctOsg.h"

std::vector<osg::Vec3d> convert(const TColgp_Array1OfPnt& points);
TColgp_Array1OfPnt convert(const std::vector<osg::Vec3d>& points);
std::vector<double> convert(const TColStd_Array1OfReal& weights);
TColStd_Array1OfReal convert(const std::vector<double>& weights);
std::vector<int> convert(const TColStd_Array1OfInteger& mults);
TColStd_Array1OfInteger convert(const std::vector<int>& mults);

struct SurfacePrivate
{
	SurfacePrivate()
		: m_rep3D(new osg::Geometry())
	{
		m_rep3D->setNodeMask(D3SurfaceMask);
		osg::ref_ptr<osg::StateSet> ss = m_rep3D->getOrCreateStateSet();
		ss->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
		ss->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
		static osg::ref_ptr<osg::BlendFunc> blendFunc = new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		ss->setAttributeAndModes(blendFunc);
	}
	osg::ref_ptr<osg::Geometry> m_rep3D;
	TopoDS_Face m_shape;
	std::vector<std::vector<osg::Vec3d>> m_edgePoints;
	std::vector<osg::Vec3d> m_handles;
	bool m_selected = false;
};

Surface::Surface(INode* parent)
	: Ent3DFace(parent)
	, d_ptr(new SurfacePrivate)
{
}

void Surface::setShape(const TopoDS_Face & shape)
{
	d_ptr->m_shape = shape;
	addFlags(UPDATE_GEOMETRY);
}

const TopoDS_Face & Surface::shape() const
{
	return d_ptr->m_shape;
}

bool getNormal(const TopoDS_Face& face, const gp_Pnt2d& uv, gp_Dir& normal, gp_Pnt& P, gp_Vec& D1U)
{
	TopLoc_Location l;
	Handle(Geom_Surface) GS = BRep_Tool::Surface(face, l);
	if (!GS.IsNull())
	{
		BRepAdaptor_Surface S;
		S.Initialize(face, false);
		gp_Vec D1V;
		gp_Vec D2U, D2V, D2UV;
		CSLib_DerivativeStatus Status;
		double U = uv.X();
		double V = uv.Y();
		S.D1(U, V, P, D1U, D1V);
		CSLib::Normal(D1U, D1V, Precision::Angular(), Status, normal);
		if (Status != CSLib_Done) {
			S.D2(U, V, P, D1U, D1V, D2U, D2V, D2UV);
			CSLib_NormalStatus NStat;
			bool OK;
			CSLib::Normal(D1U, D1V, D2U, D2V, D2UV, Precision::Angular(), OK, NStat, normal);
		}
		if (face.Orientation() == TopAbs_REVERSED)
			normal.Reverse();
		return true;
	}
	return false;
}

void getNorms(const Handle(Poly_Triangulation)& triangulation, TopAbs_Orientation orietation, osg::ref_ptr<osg::Vec3Array>& normalsAll)
{
	Poly_Connect pc(triangulation);
	const TColgp_Array1OfPnt& nodes = triangulation->Nodes();
	const Poly_Array1OfTriangle& triangles = triangulation->Triangles();
	int n[3];
	for (int i = nodes.Lower(); i <= nodes.Upper(); i++)
	{
		gp_XYZ eqPlan(0, 0, 0);
		for (pc.Initialize(i); pc.More(); pc.Next())
		{
			triangles(pc.Value()).Get(n[0], n[1], n[2]);
			gp_XYZ v1(nodes(n[1]).Coord() - nodes(n[0]).Coord());
			gp_XYZ v2(nodes(n[2]).Coord() - nodes(n[1]).Coord());
			eqPlan += (v1^v2).Normalized();
		}
		gp_Dir normal = gp_Dir(eqPlan);
		if (orietation == TopAbs_REVERSED)
			normal.Reverse();
		normalsAll->push_back(osg::Vec3d(normal.X(), normal.Y(), normal.Z()));
	}
}

void Surface::update3D()
{
	if (flags())
	{
		calHandles();
		updateEdgePoints();
		d_ptr->m_rep3D->getPrimitiveSetList().clear();
		if (d_ptr->m_shape.IsNull())
		{
			removeFlags(0xFFFFFFFF);
			return;
		}
		const double linearDeflection = 0.01; // 1 厘米
		const double angularDeflection = 0.5; // 弧度

		TopLoc_Location L;
		Handle(Poly_Triangulation) triangulation = BRep_Tool::Triangulation(d_ptr->m_shape, L);
		if (!triangulation)
		{
			BRepMesh_IncrementalMesh aMesh(d_ptr->m_shape, linearDeflection, Standard_False, angularDeflection);
			triangulation = BRep_Tool::Triangulation(d_ptr->m_shape, L);
		}
		if (triangulation)
		{
			osg::ref_ptr<osg::Vec3Array> positionsAll = new osg::Vec3Array;
			osg::ref_ptr<osg::Vec3Array> normalsAll = new osg::Vec3Array;
			osg::ref_ptr<osg::UIntArray> indicesAll = new osg::UIntArray;

			const TColgp_Array1OfPnt& nodes = triangulation->Nodes();
			int nodeNum = triangulation->NbNodes();
			//assert(nodeNum < 65536);
			// 如果>=65536，那么索引不可以用UShort表达
			//if (nodeNum < 65536)
			{
				gp_Dir normal;
				gp_Vec D1U;
				gp_Pnt P;
				for (int i = nodes.Lower(); i <= nodes.Upper(); i++)
				{
					gp_Pnt pt = nodes(i).Transformed(L.Transformation());
					positionsAll->push_back(osg::Vec3(pt.X(), pt.Y(), pt.Z()));
					gp_Pnt2d uv = triangulation->UVNode(i);
					bool ok = getNormal(d_ptr->m_shape, uv, normal, P, D1U);
					if (ok)
						normalsAll->push_back(osg::Vec3(normal.X(), normal.Y(), normal.Z()));
				}
				const Poly_Array1OfTriangle& triangles = triangulation->Triangles();
				for (int i = triangles.Lower(); i <= triangles.Upper(); i++)
				{
					Poly_Triangle pt = triangles.Value(i);
					int index1, index2, index3;
					if (d_ptr->m_shape.Orientation() == TopAbs_REVERSED)
						pt.Get(index1, index3, index2);
					else
						pt.Get(index1, index2, index3);
					indicesAll->push_back(index1 - 1);
					indicesAll->push_back(index2 - 1);
					indicesAll->push_back(index3 - 1);
				}
				// 处理getNormal失败的情况
				if (normalsAll->size() < positionsAll->size())
				{
					normalsAll->clear();
					getNorms(triangulation, d_ptr->m_shape.Orientation(), normalsAll);
				}
			}

			// 组装OSG图形对象
			d_ptr->m_rep3D->setVertexArray(positionsAll);
			d_ptr->m_rep3D->setNormalArray(normalsAll, osg::Array::BIND_PER_VERTEX);
			osg::ref_ptr<osg::Vec4Array> ca = new osg::Vec4Array();
			d_ptr->m_rep3D->setColorArray(ca.get(), osg::Array::BIND_OVERALL);
			updateColor();
			d_ptr->m_rep3D->addPrimitiveSet(new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES, indicesAll->size(), indicesAll->asVector().data()));
		}
		else
		{
			d_ptr->m_rep3D->setVertexArray(nullptr);
			d_ptr->m_rep3D->setNormalArray(nullptr, osg::Array::BIND_PER_VERTEX);
			d_ptr->m_rep3D->setColorArray(nullptr, osg::Array::Binding::BIND_OVERALL);
			d_ptr->m_rep3D->getPrimitiveSetList().clear();
		}

		d_ptr->m_rep3D->dirtyGLObjects();
		removeFlags(0xFFFFFFFF);
	}
}

osg::Node * Surface::getRep3D() const
{
	return d_ptr->m_rep3D.get();
}

void Surface::setSelected(bool val)
{
	if (d_ptr->m_selected != val)
	{
		d_ptr->m_selected = val;
		updateColor();
	}
}

void Surface::save(QJsonObject & json) const
{
	Ent3DFace::save(json);

	if (!d_ptr->m_shape.IsNull())
	{
		std::stringstream ss;
		BRepTools::Write(d_ptr->m_shape, ss);
		json["brep"] = ss.str().c_str();
	}
}

void Surface::load(const QJsonObject& json, JsonReader& reader)
{
	Ent3DFace::load(json, reader);

	if (json.contains("brep"))
	{
		BRep_Builder builder;
		std::stringstream ss;
		ss.str(json["brep"].toString().toStdString());
		BRepTools::Read(d_ptr->m_shape, ss, builder);
	}
}

void Surface::move(const osg::Vec3d& offset)
{
	gp_Trsf tf;
	tf.SetTranslation(gp_Vec(offset.x(), offset.y(), offset.z()));
	BRepBuilderAPI_Transform tfr(tf);
	tfr.Perform(d_ptr->m_shape, true);
	d_ptr->m_shape = TopoDS::Face(tfr.Shape());
	addFlags(UPDATE_GEOMETRY);
}

std::vector<osg::Vec3d> Surface::handles() const
{
	if (flags())
	{
		calHandles();
	}

	return d_ptr->m_handles;
}

std::vector<std::vector<osg::Vec3d>> Surface::getEdgePoints() const
{
	return d_ptr->m_edgePoints;
}

std::shared_ptr<Ent> Surface::clone() const
{
	auto surface = std::make_shared<Surface>(getParent());
	surface->copy(*this);
	return surface;
}

void Surface::copy(const Ent& ent)
{
	auto surface = dynamic_cast<const Surface*>(&ent);
	if (surface)
	{
		BRepBuilderAPI_Copy copy;
		copy.Perform(surface->d_ptr->m_shape);
		d_ptr->m_shape = TopoDS::Face(copy.Shape());
		addFlags(UPDATE_GEOMETRY);
	}
}

void Surface::reverse()
{
	if (d_ptr->m_shape.IsNull())
		return;
	d_ptr->m_shape.Reverse();
	addFlags(UPDATE_GEOMETRY);
}

void Surface::updateColor()
{
	auto ca = dynamic_cast<osg::Vec4Array*>(d_ptr->m_rep3D->getColorArray());
	assert(ca);
	if (ca)
	{
		ca->clear();
		osg::Vec4f color;
		if (d_ptr->m_selected)
			color = g_selectedFaceColor;
		else
			color = g_faceColor;
		color.a() = g_alpha;
		ca->push_back(color);
		ca->dirty();
		d_ptr->m_rep3D->dirtyGLObjects();
	}
}

void Surface::rotate(const double angle, const osg::Vec2d & basePoint)
{
	gp_Trsf trsf;
	gp_Pnt point = toPnt(osg::Vec3d(basePoint.x(),basePoint.y(),0));
	gp_Ax1 axe(point, gp_Dir(0.0, 0.0, 1.0));
	trsf.SetRotation(axe,angle);
	BRepBuilderAPI_Transform brepTrsf(d_ptr->m_shape,trsf,false);
	if (brepTrsf.IsDone())
	{
		setShape(TopoDS::Face(brepTrsf.Shape()));
	}
}

void Surface::updateEdgePoints()
{
	d_ptr->m_edgePoints.clear();
	if (d_ptr->m_shape.IsNull())
	{
		return;
	}
	TopExp_Explorer Ex;
	for (Ex.Init(d_ptr->m_shape, TopAbs_EDGE); Ex.More(); Ex.Next())
	{
		const TopoDS_Edge& edge = TopoDS::Edge(Ex.Current());
		if (!edge.IsNull()&&(!BRep_Tool::Degenerated(edge)))
		{
			double First, Last;
			const Handle(Geom_Curve)& c = BRep_Tool::Curve(edge, First, Last);
			std::vector<osg::Vec3d> points;
			if (c->IsKind(STANDARD_TYPE(Geom_Line)))
			{
				ShapeAnalysis_Edge anEdgeAnalyser;
				TopoDS_Vertex firstVertex = anEdgeAnalyser.FirstVertex(edge);
				TopoDS_Vertex lastVertex = anEdgeAnalyser.LastVertex(edge);
				gp_Pnt firstPnt = BRep_Tool::Pnt(firstVertex);
				gp_Pnt lastPnt = BRep_Tool::Pnt(lastVertex);
				points.push_back(osg::Vec3d(firstPnt.X(), firstPnt.Y(), firstPnt.Z()));
				points.push_back(osg::Vec3d(lastPnt.X(), lastPnt.Y(), lastPnt.Z()));
			}
			else
			{
				// 均匀折线化
				GeomAdaptor_Curve adaptedCurve(c, First, Last);

				double Deflection = 0.01;
				GCPnts_TangentialDeflection myAlgo2;
				double aDeflection = 1.0 / 36 * M_PI;
				myAlgo2.Initialize(adaptedCurve, aDeflection, Deflection);
				//if (myAlgo2.IsDone())
				{
					int nbr = myAlgo2.NbPoints();
					double param;
					gp_Pnt pt;
					for (int i = 1; i <= nbr; i++)
					{
						param = myAlgo2.Parameter(i);
						pt = myAlgo2.Value(i);
						// 半径为-1标志这是曲线的内部点
						points.push_back(osg::Vec3d(pt.X(), pt.Y(), pt.Z()));
					}
				}
			}
			d_ptr->m_edgePoints.push_back(points);
		}
	}
}

void Surface::calHandles() const
{
	d_ptr->m_handles.clear();
	if (d_ptr->m_shape.IsNull())
	{
		return;
	}
	std::vector<gp_Pnt> points;
	TopExp_Explorer Ex;
	for (Ex.Init(d_ptr->m_shape, TopAbs_VERTEX); Ex.More(); Ex.Next())
	{
		const TopoDS_Vertex& vertex = TopoDS::Vertex(Ex.Current());
		gp_Pnt pnt = BRep_Tool::Pnt(vertex);
		bool found = false;
		for (const auto& pt : points)
		{
			if (pnt.IsEqual(pt, 0.0001))
			{
				found = true;
				break;
			}
		}
		if (!found)
		{
			points.push_back(pnt);
		}
	}
	for (const auto& pt : points)
	{
		d_ptr->m_handles.push_back(osg::Vec3d(pt.X(), pt.Y(), pt.Z()));
	}
}
